package wili.attrMapping.core;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import wili.a.Refs;
import wili.attrMapping._user.b1.AttConst;
import wili.attrMapping._user.b1.AttMapingRule_main;
import wili.attrMapping._user.b1.XmlSales9;
import wili.attrMapping._user.b1.exp.AndOrExp2;
import wili.attrMapping.file.JsonFile;
import wili.attrMapping.file.XmlFile;
import wili.c.Ca;
import wili.c.Math9a;
import wili.c.Misc9a;
import wili.exp.simple.Sexp;
import wili.exp.simple.Sop;
import wili.json.xml.XmlTag;

/**
 * @author weila 2022年10月3日
 */
public abstract class XmlUserConf implements UserConf, AttConst {
	public final AttsDefine attDefine;
	protected final String ruleFile;

	public XmlUserConf(String ruleFile, AttsDefine def) {
		Att9.inc = ParseOutNewAttValue9.inc = 0;
		this.ruleFile = ruleFile;
		this.attDefine = def;
		def.conf = this;
	}

	@Override
	public abstract String replaceFileContent_atLast(String st);

	@Override
	public Atts createAtts(AttsFile afile, String key, Object obj, boolean isNewAtt) {
		return new MiscAtts(attDefine, afile, key, obj, isNewAtt);
	}

	@Override
	public AttsDefine getAttsDefine() {
		return this.attDefine;
	}

	/**
	 * 查找旧属性集
	 */
	@Override
	public List<Atts> findOldAttsList(AttsDefine def, AttsFile file_) {
		Att9.ref("查找旧属性");
		XmlFile file = (XmlFile) file_;
		List<Atts> ret = new ArrayList<>();
		List<XmlTag> oldObjList = file.findNodesByPath("//saleItems//Item");
		for (int i = 0; i < oldObjList.size(); i++) {
			XmlTag oldObj = oldObjList.get(i);
			String id = oldObj.valueOfAtt(def.keyName);
			Atts atts = this.createAtts(file, id, oldObj, false);
			{
				List<XmlTag> attNodes = oldObj.getAtts(true, def.attNames.toArray(new String[def.attNames.size()]));// 取得对象的若干属性
				//Atts atts = this.createAtts(file, guid.stringValue, obj, false);
				for (int j = 0; j < attNodes.size(); j++) {
					XmlTag nod = attNodes.get(j);
					Refs.now();//
					atts.addAtt(nod.name, nod.element.getTextTrim());
				}
			}
			ret.add(atts);
		}
		return ret;
	}

	@Override
	public List<Atts> findNewAttsList(Atts oldAtts) {
		Att9.ref("查找新属性");//
		return Arrays.asList(oldAtts);
	}

	public ValueMapingNode getMapingRule(ValueMapingNode rule) {
		if (rule != null)
			return rule;
		File file = new File(RuleDir, this.ruleFile);
		if (!file.exists() && !ruleFile.contains(".")) {
			file = new File(RuleDir, this.ruleFile + ".txt");
		}
		return this.getMapingRule(rule, file);
	}

	@Override
	public ValueMapingNode genRuleOf_oldNewAtts(boolean genRuleTemplate, Atts oldAtts, Atts newAtts,
			ValueMapingNode ruleRoot) {
		/*
		 * >>> GenTemplate 找到所有的旧属性序列、把每个旧属性序列，增加或合并到规则树， >>> GenRule
		 * 找到所有的旧属性序列、把每个旧属性序列，增加或合并到规则树，
		 */
		if (!genRuleTemplate)
			Ca.asert(oldAtts.getKind().equals(newAtts.getKind()), "");
		ValueMapingNode lastAttNod = ruleRoot.addOrMergeAtts(oldAtts, true);
		final ValueMapingNode ruleLeaf = lastAttNod.addOrMergeSon(oldAtts.objName);// 取得规则叶
		if (genRuleTemplate) {
			ruleLeaf.visitTimes++;
			return ruleRoot;
		}
		ruleLeaf.oldAtts.add(oldAtts);
		ruleLeaf.newAtts.add(newAtts);
		int inc2 = ++inc;
		/*
		i19,editable: {
		1: {=(@{formula}) && !${view>/$EditMode/viewMode}}
		in accP.fragment.json, 528a7dff-1398-4fb9-b873-d92369e866a3
		2: {=!${view>/$EditMode/viewMode} && ([undefined,null].includes(${DocEntry}) || ${VDocStatus} !== 'C' && ${VDocStatus} !== 'A' && ${VDeliveryStatus} !== 'N' || ${ObjType} !== '18' && ${ObjType} !== '19' && ${ObjType} !== '20' && ${ObjType} !== '21' && ${ObjType} !== '234000032' && ${ObjType} !== '540000006')}
		in accP.fragment.json, 134d203f-76dd-420c-afb5-42d0cad73837
		3: {=(@{formula}) && #DEFALUT_EDITABLE#}
		in accP.fragment.json, 8bb9e955-f22f-4d62-bd9c-f87e24b1afd0
		}
		 */
		if (inc2 == 143 || ruleLeaf.id == 19)
			Ca.pause();
		String newRuleSt = this.genRuleOf_oldNewAtts(oldAtts, newAtts);
		int ind = newRuleSt.indexOf('=');
		String chu = newRuleSt.substring(ind + 1);
		newRuleSt = newRuleSt.substring(0, ind);
		String newFileName = newAtts.file.file().getName();
		if (Att9.DEBUG)
			Ca.log("ruleID=" + ruleLeaf.id);
		String st = newRuleSt + "=" + chu + ", id=" + oldAtts.key;
		{//若生成公式同已有公式不同，则视为冲突
			if (ruleLeaf.value == null || ruleLeaf.value.isEmpty()) {
				// 没有规则时，新规则就是规则
				//String va = B1com.andExpToFormular(newRuleSt, ruleLeaf.value);
				ruleLeaf.setValue(newRuleSt);
				ruleLeaf.addValue(newRuleSt, newAtts.key, newFileName);
			} else {
				if (Math9a.compare(ruleLeaf.value, ">=", newRuleSt)) {
				} else {
					ruleLeaf.setValue(newRuleSt);
				}
			}
		}
		{// 某fromId 未找到对应的toID, 但当前规则可能已经有值，仅pop此id.
		 // lastAttNod.oldAtts.pop();//
		}
		AttMapingRule_main.scanNum++;
		newAtts.updateValue(ruleLeaf.value);
		ruleRoot.print(-1);
		return lastAttNod;
	}

	@Override
	public Set<AttsFile> valueOfNewAtts_inOldFile(boolean isCheck, AttsFile oldFile, AttsDefine attsDefine,
			ValueMapingNode ruleRoot, AttMapingRule_main cont) {
		/*
		 * update: 找到所有的旧属性序列、把每个旧属性序列，合并到规则树， 若规则先行无子，意味着：非预期的属性序列（规则路径）。
		 * 
		 */
		Set<AttsFile> fileSet = new HashSet<>();
		UserConf conf = attsDefine.conf;
		AttsDefine def = conf.getAttsDefine();
		List<Atts> oldAttsList = conf.findOldAttsList(def, oldFile);
		List<String> attList = attsDefine.attNames;
		Collections.sort(attList);// 顺序重要，便于同实例同步。
		//
		Ca.pause();
		String targetAtt = conf.getAttsDefine().targetAtt;
		for (int k = 0; k < oldAttsList.size(); k++) {
			++inc;
			// 根据对象名取得旧对象集、对每个对象，取得其属性集。
			Atts oldAtts = oldAttsList.get(k);
			AttMapingRule_main.oldNum++;
			if (isCheck) {
			} else {// 行至规则叶后，取得新属性对象，（叶值+旧属性）~>新属性值，把新属性值刷新到对象中。
				// ValueMapingNode ruleLeaf = ruleRoot.addOrMergeAtts(oldAtts, false);
				if ("847c4445-12nn-35c0-a789-b59bb1fc4a20".equals(oldAtts.key))
					Ca.pause();
				List<Atts> newAttsList = oldAtts.findNewAttsList();
				if (newAttsList.isEmpty()) {
					if (inc < 5)
						Att9.log7("\n WARN_", Bugs.MappingToNotFound, oldAtts);
					continue;
				} //
				String oldKind = oldAtts.getKind();
				for (Atts newAtts : newAttsList) {
					if (!oldKind.equals(newAtts.getKind()))
						Ca.asert(false, "never");
					conf.valueOfNewAtts(oldAtts, ruleRoot, newAtts);// <<<<<<<<<<<<<<<<<<<<<<<<<
					fileSet.add(newAtts.file);
				}
			}
		}
		return fileSet;
	}

	@Override
	public void valueOfNewAtts(Atts oldAtts, ValueMapingNode ruleRoot, Atts newAtts) {
		//更新值的时候：规则值 vs 旧值 ~>新值
		Att9.ref("新属性=fun(规则，旧属性)");//规则值比对计算值，若不合规则报警
		ValueMapingNode ruleLeaf;
		{// 顺着属性序列走向规则叶
			Map<String, Object> cont = oldAtts.toValueMap();
			ruleLeaf = oldAtts.getRuleTarget(ruleRoot);
			if (ruleLeaf == null)
				return;
			ruleLeaf = ruleLeaf.son(newAtts.def.targetAtt, null);
			if ("".isEmpty()) {
				if (++inc == 23)
					Ca.pause();
				String idSt = ",\tid=\"" + oldAtts.key + "\"";
				String[] rules = ruleLeaf.value.split("\\s*;\\s*");
				if (rules.length == 2)
					Ca.pause();
				for (String rule : rules) {
					Sexp exp = Sexp.newInst(rule);
					AndOrExp2 exp2 = AndOrExp2.newInst(1, rule);//用这个比较好
					if (!exp2.isTrue(cont)) {
						++violationInc;
						if (exp instanceof Sop) {
							Number ll = exp.left().value(cont);
							String st = exp.toNumString(cont) + ",\tis " + Misc9a.preciseOfNum(ll.doubleValue(), 3);
							Ca.log(violationInc + ", WARN: expect " + rule + ",\tbut: " + st + idSt);
						} else {
							Ca.log(violationInc + ", WARN: value is: " + exp.value(cont) + idSt);
						}
					}
				}
				return;
			}
		}
		if (false)
			this.attDefine.valueOfNewAtt(ruleLeaf.value, oldAtts);
		{
			Att inv = oldAtts.att(XmlSales9.INV);
			Att quan = oldAtts.att(XmlSales9.QUAN);
			double inv2 = Double.parseDouble(inv.value);
			double quan2 = Double.parseDouble(quan.value);
			double rate = quan2 / inv2;
			String ret = String.valueOf(Misc9a.preciseOfNum(rate, 2));
			String st = "=" + quan2 + "/" + inv2 + ", id=\"" + oldAtts.key + "\"";
			if (Math9a.compare(ruleLeaf.value, ">=", ret)) {
				Ca.log(-1, "\tok: expect maxRate=", ruleLeaf.value, "≥", ret + st);
			} else {
				++violationInc;
				Ca.log(violationInc + ", WARN: expect maxRate=" + ruleLeaf.value + "<" + ret + st);
			}
		}
	}

	public AttsFile attsFileOf(Object key) {
		if ("".isEmpty())
			return null;
		AttsFile ret = MAP.get(key);
		if (ret == null) {
			if (key instanceof File) {
				MAP.put(key, ret = new JsonFile((File) key));
			} else {
				//MAP.put(key, ret = new JsonFile((String) key));
			}
		}
		return ret;
	}

	private Map<Object, AttsFile> MAP = new HashMap<>();
	private static int errInc, inc, violationInc;
}
